use std::borrow::Borrow;
use std::str::FromStr;

use actix_session::Session;
use actix_web::web::{self, ServiceConfig};
use wither::bson::oid::ObjectId;

use crate::errors::cons_error;
use crate::middlewares::authorize::club_getters::default_getter;
use crate::middlewares::authorize::Authorize;
use crate::models::user::{NormalUserModel, UserActor, UserActorDTO, UserModelDTO, UserRole};
use crate::models::user_application::UserApplicationModelDTO;
use crate::services::club_service::ClubService;
use crate::services::form_service::FormService;
use crate::services::user_service::CreateUserReq;
use crate::services::user_service::GetUserRolesReq;
use crate::services::user_service::UpdateUserInfoReq;
use crate::services::user_service::UserInfoOptions;
use crate::utils::authorize::{
    check_user_has_permission_with_object_id, check_user_has_section_permission,
};
use crate::utils::db_utils::{build_filter, COMMON_NO_REGEXP};
use crate::AppError;
use crate::{services::user_service::UserService, AppResult, PaginationQuery};
use actix_web::HttpResponse;

use super::OK_RESPONSE;

pub async fn search_all_users(
    user_service: web::Data<UserService>,
    query: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::FindAllUsersReq>,
) -> AppResult<HttpResponse> {
    let res = user_service
        .search_all_users(
            req.get_filter_as_document_use_default(),
            &query.into_inner(),
        )
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<UserModelDTO>()))
}
pub async fn create_user(
    user_service: web::Data<UserService>,
    query: web::Json<CreateUserReq>,
) -> AppResult<HttpResponse> {
    Ok(HttpResponse::Ok().json(UserModelDTO::from(
        user_service.create_user(&query.into_inner()).await?,
    )))
}

/// ## update_user_info
/// 更新用户信息
pub async fn update_user_info(
    user_service: web::Data<UserService>,
    session: Session,
    req: web::Json<UserInfoOptions>,
) -> AppResult<HttpResponse> {
    let info_options = req.into_inner();
    let user = UserService::get_user_from_session(&session)?;
    let user_id = user
        .id
        .ok_or(AppError::ConsError(cons_error::MSG_USER_BREAK))?
        .to_string();
    let user = user_service
        .update_user_info(&UpdateUserInfoReq {
            user_id,
            info: info_options,
        })
        .await?;
    user_service.update_user_in_session(user).await?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}

/// ## get_user_info
/// 获取用户信息
/// `/api/authorize/self` `[get]`
async fn get_user_info(session: Session) -> AppResult<HttpResponse> {
    let user = UserService::get_user_from_session(&session)?;
    Ok(HttpResponse::Ok().json(UserModelDTO::from(user)))
}
/// ## get_user_roles
/// 获取用户权限信息
async fn get_user_actors(
    user_service: web::Data<UserService>,
    session: Session,
    req: web::Query<GetUserRolesReq>,
) -> AppResult<HttpResponse> {
    let user = UserService::get_user_from_session(&session)?;
    let req = req.into_inner();
    let roles = user_service
        .get_user_roles(
            user.id
                .ok_or(AppError::ConsError(cons_error::MSG_USER_BREAK))?,
            &req,
        )
        .await?;

    Ok(HttpResponse::Ok().json(
        roles
            .into_iter()
            .map(UserActorDTO::from)
            .collect::<Vec<_>>(),
    ))
}

/// ## check_user_role
/// 检查用户权限
async fn check_user_role(
    user_service: web::Data<UserService>,
    session: Session,
) -> AppResult<HttpResponse> {
    let user_id = UserService::get_user_from_session(&session)?
        .id
        .ok_or(AppError::ConsError(cons_error::MSG_USER_BREAK))?;
    Ok(HttpResponse::Ok().json(user_service.check_user_role(user_id).await?))
}
/// ## 检查用户是否有授予权限的权限
fn check_auth<T: Borrow<NormalUserModel>>(
    actor: &UserActorDTO,
    user: T,
) -> AppResult<(bool, Option<ObjectId>, Option<ObjectId>)> {
    let club_id = actor
        .club_id
        .as_deref()
        .map(ObjectId::from_str)
        .and_then(Result::ok);
    let section_id = actor
        .article_section_id
        .as_deref()
        .map(ObjectId::from_str)
        .and_then(Result::ok);
    let res = match (club_id, section_id) {
        (Some(club_id), None) => (
            check_user_has_permission_with_object_id(user, club_id, UserRole::LeaderOrAdmin),
            Some(club_id),
            None,
        ),
        (None, Some(section_id)) => (
            check_user_has_section_permission(user, section_id, UserRole::LeaderOrAdmin),
            None,
            Some(section_id),
        ),
        (None, None) => (
            check_user_has_permission_with_object_id(user, None, UserRole::Admin),
            None,
            None,
        ),
        _ => {
            return Err(AppError::ConsError(
                cons_error::MSG_NOT_ALLOW_GIVE_MULTI_TYPE_AUTH,
            ));
        }
    };
    Ok(res)
}
/// ## 为用户添加角色权限
async fn add_actor_to_user(
    user_service: web::Data<UserService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::AddUserActorReq>,
) -> AppResult<HttpResponse> {
    let user_id = ObjectId::from_str(&req.user_id)?;
    let (ok, club_id, article_section_id) = check_auth(&req.actor, user)?;
    if !ok {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let actor = UserActor {
        club_id,
        article_section_id,
        role: req.actor.role,
    };
    let res = user_service.add_actor_to_user(user_id, actor).await?;
    Ok(HttpResponse::Ok().json(UserModelDTO::from(res)))
}

/// ## 移除用户的角色权限
async fn remove_actor_from_user(
    user_service: web::Data<UserService>,
    club_service: web::Data<ClubService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::RemoveUserActorReq>,
) -> AppResult<HttpResponse> {
    let user_id = ObjectId::from_str(&req.user_id)?;
    let (ok, club_id, article_section_id) = check_auth(&req.actor, user)?;
    if !ok {
        return Err(AppError::ConsError(cons_error::MSG_REQUIRE_AUTHORIZE));
    }
    let actor = UserActor {
        club_id,
        article_section_id,
        role: req.actor.role,
    };
    let res = user_service.remove_actor_from_user(user_id, actor).await?;
    if let Some(club_id) = club_id {
        if !club_service.check_club_has_leader(club_id).await? {
            // 没有社长进入隐藏状态
            club_service.set_club_to_hidden(club_id).await?;
        }
    }
    Ok(HttpResponse::Ok().json(UserModelDTO::from(res)))
}

async fn search_all_applications(
    user: NormalUserModel,
    form_service: web::Data<FormService>,
    page: web::Query<PaginationQuery>,
    req: web::Json<ctrl_struct::FindAllApplicationCtrlReq>,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let mut filter = req
        .where_value
        .and_then(|v| build_filter(&v, &COMMON_NO_REGEXP))
        .unwrap_or_default();
    filter.insert("user_id", user.id);
    let res = form_service
        .find_all_application(page.into_inner(), filter)
        .await?;
    Ok(HttpResponse::Ok().json(res.map_into::<UserApplicationModelDTO>()))
}
pub fn configure_user_controller(config: &mut ServiceConfig) {
    config
        .service(
            web::scope("list")
                .wrap(Authorize::new(UserRole::Admin, default_getter))
                .route("", web::post().to(search_all_users))
                .route("/add", web::post().to(create_user)),
        )
        .service(
            web::scope("/self")
                .service(
                    web::scope("/info")
                        .route("", web::post().to(get_user_info))
                        .route("/update", web::post().to(update_user_info)),
                )
                .service(
                    web::scope("/actor")
                        .route("", web::post().to(get_user_actors))
                        .route("/check", web::post().to(check_user_role))
                        .route("/add", web::post().to(add_actor_to_user))
                        .route("/remove", web::post().to(remove_actor_from_user)),
                )
                .service(
                    web::scope("/application")
                        .route("search", web::post().to(search_all_applications)),
                ),
        );
}

mod ctrl_struct {
    use serde::{Deserialize, Serialize};
    use serde_json::Value;

    use crate::models::user::UserActorDTO;
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct FindAllApplicationCtrlReq {
        #[serde(rename = "filter")]
        pub where_value: Option<Value>,
    }
    define_filter_request_struct!(FindAllUsersReq {});
    define_request_struct!(AddUserActorReq {
        user_id: String,
        actor: UserActorDTO
    });
    define_request_struct!(RemoveUserActorReq {
        user_id: String,
        actor: UserActorDTO
    });
}
